package warthog.parsegen;

import warthog.cradle.JavaClass;


public class Rule {
	final Sym head;
	final Sym[] body;
	String note;
	final int precedence;
	final private Dot[] dots;
	final int id;
	public int line=0;
	public Rule(int id, Sym head, Sym[] rhs, int precedence) {
		this.id = id;
		this.head = head;
		this.body = rhs;
		this.precedence = precedence;
		// We keep the dots unique, though this is not necessary anymore...
		this.dots = new Dot[rhs.length+1];
		for (int i=0; i<dots.length; i++) dots[i] = new Dot(this, i);
		// The new rule registers with its head symbol. 
		head.produce(this);
	}
	public int number() { return -1-id; }
	public Dot dot(int offset) {
		if (offset >= 0 && offset < dots.length) return dots[offset];
		else return null;
	}
	public boolean is_lambda() {
		for (Sym s: body) if (!s.lambda) return false;
		return true;
	}
	public Dot front() { return this.dots[0]; }
	public String toString() {
		StringBuilder sb = new StringBuilder("<"+line+"> "+head+" ->");
		for (Sym s:body) sb.append(" "+s.toString());
		return sb.toString();
	}
	public Sym unitness() { return isSyntaxUnit() ? head : null; }
	public boolean isEpsilon() {
		return body.length==0;
	}
	public void reduceCode(JavaClass cw) {
		if (body.length != 0) cw.codeln("yysp-="+body.length+"; yyq=yyqs[yysp];");
		// To write the state shift requires knowing the rule's token code... 
		// It's not going to happen here. It will happen in the reduce() function.
		if (isUnit()) {
			// Nothing more to do
		} else {
			writePops(cw);
			head.writeDeclareHead(cw);
			if (note != null) {
				cw.code(note);
				if (head.isVoid() && note.contains("$$")) {
					System.err.println("Warning: you've mentioned $$ in "+this+" but it's meaningless.");
				}
				if (!head.isVoid() && !note.contains("$$")) {
					System.err.println("Warning: you've failed to mention $$ in "+this+" but it has type "+head.semanticClass.jt);
				}
			} else write_generic_rule(cw);
			head.writePush(cw);
		}
	}
	private void write_generic_rule(JavaClass cw) {
		int i = indexOfFirstNonVoidComponent();
		boolean found = i<body.length;
		if (found) {
			if (head.isVoid()) {
				System.err.println("Warning: "+this+" has typed components, no type itself, and no semantic action.");
			} else cw.codeln("$$ = $"+(i+1)+";");
		} else if (!head.isVoid()) {
			System.err.println("Need a semantic action to provide a value of type "+head.semanticClass.jt+" in rule: "+this);
			System.exit(1);
		}
	}
	private int indexOfFirstNonVoidComponent() {
		int i = 0;
		while (i<body.length && body[i].isVoid()) i++;
		return i;
	}
	private void writePops(JavaClass cw) {
		int i = body.length;
		while (i-->0) body[i].writePop(cw, i+1);
		cw.codeln();
	}
	private boolean isUnit() {
		return isSyntaxUnit() && (body[0].semanticClass == head.semanticClass) ;
	}
	private boolean isSyntaxUnit() { return (body.length == 1) && (note == null); }
	boolean inferType() {
		if (head.isVoid() && note == null) {
			int i = indexOfFirstNonVoidComponent();
			if (i<body.length) {
				head.semanticClass = body[i].semanticClass;
				return true;
			}
		}
		return false;
	}
}